package com.hfi.manongfanshen;

import java.util.Arrays;

/**
 * SimpleList类 {@link java.util.ArrayList}
 * 模仿Java 的ArrayList 已经定义好了接口 需要实现具体的功能。
 * <p>
 * 这道题目不要求实现复杂的算法，不需要进行面向对象的设计，也不需要考虑多线程下的线程安全(modCount的作用)
 * 况且已经给出了提示：用一个Object 数组来实现。
 * 如果说有一点语法层面的难度，那就是使用了一点泛型。
 * 其实，如果我把泛型去掉，估计他们还是实现不了，因为这个类的核心逻辑不是泛型。
 * </p>
 * <p>
 * 这个SimpleList类要求的是能对一个数组进行操作，核心逻辑就两点：
 * 1. 能往数组里添加数据，记录size 。如果数组空间不够了，则需要增大空间。
 * 2. 能删除数组指定位置的数据，并且把之后的数据往前挪动。
 * </p>
 * <p>
 * 这里面有很多细节需要处理，一不留神就会出错一一计算机编程就是这样的
 * 人类语言的需求：电脑，我要创建一个类似java ArrayList的类 包含 get add remove方法  并且这个ArrayList类的容量不是固定的 能够自增长
 * 养成计算机的思维方式，流畅地把人类语言的需求转换成计算机语言
 * </p>
 *
 * @author ChangLiang
 * @date 2019/11/28
 */
public class SimpleList<T> {

    public SimpleList() {
        elementData = new Object[DEFAULT_CAPACITY];
    }

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 2;

    /**
     * Shared empty array instance used for empty instances.
     */
    private Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;

    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 添加元素
     *
     * @param e
     * @return
     */
    public boolean add(T e) {
        // ensureCapacity
        ensureCapacity(size + 1);
        elementData[size++] = e;
        return true;
    }

    /**
     * 移除元素
     *
     * @param o
     * @return
     */
    public boolean remove(Object o) {
        // 找到要删除的位置
        if (o == null) {
            for (int index = 0; index < size; index++) {
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
            }
        } else {
            for (int index = 0; index < size; index++) {
                if (elementData[index].equals(o)) {
                    fastRemove(index);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 后面的逐个前移 来填洞
     * @param index
     */
    private void fastRemove(int index) {
        // 要移动的元素个数
        int numMoved = size - index - 1;
        if (numMoved > 0) {
            System.arraycopy(elementData,index+1,elementData,index,numMoved);
        }
        // clear to let GC do its work
        // size-1 并将这个元素内容置为null
        elementData[--size] = null;
    }

    /**
     * 获取元素
     *
     * @param index
     * @return
     */
    public T get(int index) {
        // 不判断true or false 而选择输入不正确 直接抛出异常
        rangeCheck(index);
        return (T) elementData[index];
    }

    /**
     * Checks if the given index is in range.  If not, throws an appropriate
     * runtime exception.  This method does *not* check if the index is
     * negative: It is always used immediately prior to an array access,
     * which throws an ArrayIndexOutOfBoundsException if index is negative.
     */
    private void rangeCheck(int index) {
        if (index < 0 || index >= elementData.length) {
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    }

    /**
     * Constructs an IndexOutOfBoundsException detail message.
     * Of the many possible refactorings of the error handling code,
     * this "outlining" performs best with both server and client VMs.
     */
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size;
    }

    private void ensureCapacity(int minCapacity) {
        if (minCapacity - elementData.length > 0) {
            grow(minCapacity);
        }
    }

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        // 位移运算 右移 除以2
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            newCapacity = MAX_ARRAY_SIZE;
        }
        System.out.println("grow from " + oldCapacity + " to " + newCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
}
